﻿#region Copyright (c) 2000-2008 Developer Express Inc.
/*
{*******************************************************************}
{                                                                   }
{       Developer Express .NET Component Library                    }
{       AgDataGrid                                 }
{                                                                   }
{       Copyright (c) 2000-2008 Developer Express Inc.              }
{       ALL RIGHTS RESERVED                                         }
{                                                                   }
{   The entire contents of this file is protected by U.S. and       }
{   International Copyright Laws. Unauthorized reproduction,        }
{   reverse-engineering, and distribution of all or any portion of  }
{   the code contained in this file is strictly prohibited and may  }
{   result in severe civil and criminal penalties and will be       }
{   prosecuted to the maximum extent possible under the law.        }
{                                                                   }
{   RESTRICTIONS                                                    }
{                                                                   }
{   THIS SOURCE CODE AND ALL RESULTING INTERMEDIATE FILES           }
{   ARE CONFIDENTIAL AND PROPRIETARY TRADE                          }
{   SECRETS OF DEVELOPER EXPRESS INC. THE REGISTERED DEVELOPER IS   }
{   LICENSED TO DISTRIBUTE THE PRODUCT AND ALL ACCOMPANYING .NET    }
{   CONTROLS AS PART OF AN EXECUTABLE PROGRAM ONLY.                 }
{                                                                   }
{   THE SOURCE CODE CONTAINED WITHIN THIS FILE AND ALL RELATED      }
{   FILES OR ANY PORTION OF ITS CONTENTS SHALL AT NO TIME BE        }
{   COPIED, TRANSFERRED, SOLD, DISTRIBUTED, OR OTHERWISE MADE       }
{   AVAILABLE TO OTHER INDIVIDUALS WITHOUT EXPRESS WRITTEN CONSENT  }
{   AND PERMISSION FROM DEVELOPER EXPRESS INC.                      }
{                                                                   }
{   CONSULT THE END USER LICENSE AGREEMENT FOR INFORMATION ON       }
{   ADDITIONAL RESTRICTIONS.                                        }
{                                                                   }
{*******************************************************************}
*/
#endregion Copyright (c) 2000-2008 Developer Express Inc.

using DevExpress.AgDataGrid.Data;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System;
using System.Windows.Shapes;
using System.Windows.Media;
using DevExpress.AgDataGrid.Utils;
namespace DevExpress.AgDataGrid.Internal {
	public class AgStackPanel : StackVisibleIndexPanel, IFrameworkElement {
		public const int DefaultWidth = 100, DefaultHeight = 100;
		public static readonly DependencyProperty StretchChildrenProperty = DependencyProperty.Register("StretchChildren", typeof(bool),
			typeof(AgStackPanel), new PropertyMetadata(OnStretchChildrenPropertyChanged));
		bool isLoaded;
		Size childMaxSize;
		public AgStackPanel() : this(Orientation.Horizontal) { }
		public AgStackPanel(Orientation orientation)
			: base() {
			this.SetValueNoCallback(OrientationProperty, orientation);
			Loaded += OnLoaded;
			this.childMaxSize = new Size();
		}
		public bool StretchChildren {
			get { return (bool)GetValue(StretchChildrenProperty); }
			set { SetValue(StretchChildrenProperty, value); }
		}
		public bool IsLoaded { get { return isLoaded; } }
		public bool IsVertical { get { return Orientation == Orientation.Vertical; } }
		protected Size ChildMaxSize { get { return childMaxSize; } }
		public EventHandler<AgFreeSpaceAvailableEventArgs> FreeSpaceAvailable;
		static void OnOrientationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			if (d.IsHandlerSuspended(OrientationProperty)) return;
			AgStackPanel panel = d as AgStackPanel;
			if (panel == null) return;
			panel.OnOrientationChanged((Orientation)e.NewValue, (Orientation)e.OldValue);
		}
		static void OnStretchChildrenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			if (d.IsHandlerSuspended(StretchChildrenProperty)) return;
			AgStackPanel panel = d as AgStackPanel;
			if (panel == null) return;
			panel.OnStretchChildrenChanged((bool)e.NewValue, (bool)e.OldValue);
		}
		protected virtual void OnOrientationChanged(Orientation newValue, Orientation oldValue) {
			this.InvalidateMeasureEx();
		}
		protected virtual void OnStretchChildrenChanged(bool newValue, bool oldValue) {
			this.InvalidateMeasureEx();
		}
		protected virtual void OnLoaded(object sender, RoutedEventArgs e) {
			isLoaded = true;
		}
		protected override Size MeasureSortedChildrenOverride(Size availableSize, IList<UIElement> sortedChildren) {
			if (sortedChildren.Count == 0 || Visibility == Visibility.Collapsed || availableSize.Width == 0 || availableSize.Height == 0) {
				Size res = base.MeasureSortedChildrenOverride(availableSize, sortedChildren);
				if (res.Height == 0 && !double.IsNaN(Height) && Height != 0) res.Height = Height;
				if (res.Width == 0 && !double.IsNaN(Width) && Width != 0) res.Width = Width;
				return res;
			}
			double coord = 0;
			childMaxSize = new Size();
			foreach (UIElement child in sortedChildren) {
				child.MeasureEx(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
				Size desiredSize = child.GetDesiredSize();
				coord += IsVertical ? desiredSize.Height : desiredSize.Width;
				CalcChildMaxSize(child, desiredSize);
			}
			this.childMaxSize.Width = Math.Max(ChildMaxSize.Width, MinWidth);
			this.childMaxSize.Height = Math.Max(ChildMaxSize.Height, MinHeight);
			return IsVertical ? new Size(ChildMaxSize.Width, coord) : new Size(coord, ChildMaxSize.Height);
		}
		protected virtual void CalcChildMaxSize(UIElement child, Size desiredSize) {
			if (desiredSize.Width > ChildMaxSize.Width)
				this.childMaxSize.Width = desiredSize.Width;
			if (desiredSize.Height > ChildMaxSize.Height)
				this.childMaxSize.Height = desiredSize.Height;
		}
		protected override Size ArrangeSortedChildrenOverride(Size finalSize, IList<UIElement> sortedChildren) {
			return ArrangeOverrideCore(finalSize, sortedChildren);
		}
		protected virtual Size ArrangeOverrideCore(Size finalSize, IList<UIElement> sortedChildren) {
			if (Visibility == Visibility.Collapsed) return finalSize;
			double coord = 0;
			Rect childRect = new Rect();
			int childIndex = 0;
			foreach (UIElement child in GetArrangeList(sortedChildren)) {
				if (child.DesiredSize.Width < 1 || child.DesiredSize.Height < 1)
					child.Measure(finalSize);
				childRect.X = IsVertical ? 0 : coord;
				childRect.Y = IsVertical ? coord : 0;
				childRect.Width = child.DesiredSize.Width;
				childRect.Height = child.DesiredSize.Height;
				coord += IsVertical ? child.DesiredSize.Height : child.DesiredSize.Width;
				ArrangeChild(child, childIndex, childRect);
				childIndex++;
			}
			double finalSizeCoord = IsVertical ? finalSize.Height : finalSize.Width;
			if (coord < finalSizeCoord)
				OnFreeSpaceAvailable(finalSize, finalSizeCoord - coord);
			return DesiredSize;
		}
		protected virtual Rect ArrangeChild(UIElement child, int childIndex, Rect childRect) {
			if (StretchChildren) {
				FrameworkElement elem = child as FrameworkElement;
				if (IsVertical && childRect.Width < ChildMaxSize.Width) {
					childRect.Width = ChildMaxSize.Width;
				}
				if (!IsVertical && childRect.Height < ChildMaxSize.Height) {
					childRect.Height = ChildMaxSize.Height;
				}
			}
			child.Arrange(childRect);
			return childRect;
		}
		protected virtual IEnumerable<UIElement> GetArrangeList(IList<UIElement> sortedChildren) { return sortedChildren as IEnumerable<UIElement>; }
		protected virtual void OnFreeSpaceAvailable(Size finalSize, double sizeAvailable) {
			if (FreeSpaceAvailable != null)
				FreeSpaceAvailable(this, new AgFreeSpaceAvailableEventArgs(finalSize, sizeAvailable));
		}
		#region IFrameworkElement Members
		Size IFrameworkElement.Measure(Size availableSize) {
			return MeasureOverride(availableSize);
		}
		void IFrameworkElement.InvalidateMeasureEx() {
			for (int i = 0; i < Children.Count; i++) {
				Children[i].InvalidateMeasureEx();
			}
		}
		#endregion
		protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer() {
			return new DevExpress.AgDataGrid.UIAutomation.AgFrameworkElementAutomationPeer(this);
		}
	}
	public interface IAgStackPanelChildrenOwner {
		UIElementCollection Children { get; }
		bool IsLine(Rectangle rect);
	}
	public enum AgStretchLine { ByPreviousElement, ByPreviousAndNextElements, ByPanel }
	public class AgLineStackPanel : AgStackPanel, IAgStackPanelChildrenOwner {
		public static readonly DependencyProperty ShowLinesProperty = DependencyProperty.Register("ShowLines", typeof(bool),
			typeof(AgLineStackPanel), new PropertyMetadata(OnPanelPropertyChanged));
		public static readonly DependencyProperty LinesFillProperty = DependencyProperty.Register("LinesFill", typeof(Brush),
		   typeof(AgLineStackPanel), new PropertyMetadata(OnPanelPropertyChanged));
		public static readonly DependencyProperty LinesWidthProperty = DependencyProperty.Register("LinesWidth", typeof(double),
		   typeof(AgLineStackPanel), new PropertyMetadata(OnPanelPropertyChanged));
		public static readonly DependencyProperty LinesMarginProperty = DependencyProperty.Register("LinesMargin", typeof(double),
		   typeof(AgLineStackPanel), new PropertyMetadata(OnPanelPropertyChanged));
		public static readonly DependencyProperty RenderLastLineProperty = DependencyProperty.Register("RenderLastLine", typeof(bool),
			typeof(AgLineStackPanel), new PropertyMetadata(OnPanelPropertyChanged));
		public static readonly DependencyProperty StretchLinesProperty = DependencyProperty.Register("StretchsLineProperty", typeof(AgStretchLine),
			typeof(AgLineStackPanel), new PropertyMetadata(OnPanelPropertyChanged));
		static void OnPanelPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			if (d.IsHandlerSuspended(e.Property)) return;
			AgLineStackPanel panel = d as AgLineStackPanel;
			if (panel == null) return;
			panel.OnPanelPropertyChanged(e.Property, e.NewValue, e.OldValue);
		}
		List<Rectangle> lines;
		int elementCount;
		Dictionary<UIElement, bool> cachedLines;
		public event ShowLineEventHandler ShowLine;
		public AgLineStackPanel() : this(Orientation.Horizontal) { }
		public AgLineStackPanel(Orientation orientation)
			: base(orientation) {
			this.lines = new List<Rectangle>();
			this.cachedLines = new Dictionary<UIElement, bool>();
			this.elementCount = 0;
			this.SetValueNoCallback(LinesFillProperty, new SolidColorBrush(Colors.LightGray));
			this.SetValueNoCallback(RenderLastLineProperty, true);
			this.SetValueNoCallback(StretchLinesProperty, AgStretchLine.ByPanel);
			this.SetValueNoCallback(LinesWidthProperty, 1.0);
		}
		public bool ShowLines {
			get { return (bool)GetValue(ShowLinesProperty); }
			set { SetValue(ShowLinesProperty, value); }
		}
		public Brush LinesFill {
			get { return (Brush)GetValue(LinesFillProperty); }
			set { SetValue(LinesFillProperty, value); }
		}
		public double LinesWidth {
			get { return (double)GetValue(LinesWidthProperty); }
			set { SetValue(LinesWidthProperty, value); }
		}
		public double LinesMargin {
			get { return (double)GetValue(LinesMarginProperty); }
			set { SetValue(LinesMarginProperty, value); }
		}
		public bool RenderLastLine {
			get { return (bool)GetValue(RenderLastLineProperty); }
			set { SetValue(RenderLastLineProperty, value); }
		}
		public AgStretchLine StretchLines {
			get { return (AgStretchLine)GetValue(StretchLinesProperty); }
			set { SetValue(StretchLinesProperty, value); }
		}
		protected List<Rectangle> Lines { get { return lines; } }
		protected Dictionary<UIElement, bool> CachedLines { get { return cachedLines; } }
		protected int ElementCount { get { return elementCount; } }
		protected int LineCount {
			get {
				if (ElementCount <= 0) return 0;
				return ElementCount - (RenderLastLine ? 0 : 1);
			}
		}
		protected override Size MeasureSortedChildrenOverride(Size availableSize, IList<UIElement> sortedChildren) {
			PrepareLines(sortedChildren);
			Size result = base.MeasureSortedChildrenOverride(availableSize, sortedChildren);
			MeasureLines(sortedChildren);
			return result;
		}
		protected override IEnumerable<UIElement> GetArrangeList(IList<UIElement> sortedChildren) {
			if (!ShowLines) return base.GetArrangeList(sortedChildren);
			List<UIElement> result = new List<UIElement>();
			int elementIndex = 0;
			object nextContent = null;
			for (int i = 0; i < sortedChildren.Count; i++) {
				if (!IsPanelLine(sortedChildren[i])) {
					result.Add(sortedChildren[i]);
					Rectangle line = GetLineForElement(elementIndex);
					if (line != null) {
						if (ShowLine != null) {
							int j = i + 1;
							while (j < sortedChildren.Count && IsPanelLine(sortedChildren[j])) j++;
							nextContent = j < sortedChildren.Count ? sortedChildren[j] : null;
							ShowLineEventArgs args = new ShowLineEventArgs(sortedChildren[i], nextContent);
							ShowLine(this, args);
							if (!args.ShowLine) line.Opacity = 0;
						}
						result.Add(line);
					}
					elementIndex++;
				}
			}
			return result as IEnumerable<UIElement>;
		}
		protected virtual void OnPanelPropertyChanged(DependencyProperty dependencyProperty, object p, object p_3) {
			this.InvalidateMeasureEx();
		}
		int GetElementCount() {
			int count = 0;
			foreach (UIElement elem in Children) {
				if (IsPanelLine(elem)) continue;
				if (ArrangeAccordingToVisibleIndex)
					if (OrderPanelBase.IsInvisibleIndex(OrderPanelBase.GetVisibleIndex(elem))) continue;
				count++;
			}
			return count;
		}
		protected virtual void PrepareLines(IList<UIElement> sortedChildren) {
			Lines.Clear();
			Lines.AddRange(GetExistingLines());
			this.elementCount = GetElementCount();
			AddLines(sortedChildren);
			RemoveLines();
			HideShowLines();
			UpdateCachedLines();
		}
		protected virtual Rectangle CreateLine() {
			Rectangle line = new Rectangle();
			SetVisibleIndex(line, int.MaxValue);
			line.Fill = LinesFill;
			return line;
		}
		protected virtual bool IsPanelLine(UIElement element) { return CachedLines.ContainsKey(element); }
		protected void MeasureLines(IList<UIElement> sortedChildren) {
			if (!ShowLines) return;
			for (int i = 0; i < LineCount; i++) {
				if (i < sortedChildren.Count) {
					FrameworkElement element = GetElementForLine(i, sortedChildren);
					if (element != null) {
						SetLineWidth(Lines[i], i, sortedChildren);
					}
				}
			}
		}
		protected override void CalcChildMaxSize(UIElement child, Size desiredSize) {
			if (IsPanelLine(child)) return;
			base.CalcChildMaxSize(child, desiredSize);
		}
		void AddLines(IList<UIElement> sortedChildren) {
			if (!ShowLines) return;
			for (int i = Lines.Count; i < LineCount; i++) {
				Rectangle line = CreateLine();
				Lines.Add(line);
				Children.Add(line);
				sortedChildren.Add(line);
			}
		}
		void RemoveLines() {
			if (ShowLines) return;
			for (int i = 0; i < Lines.Count; i++) {
				Children.Remove(Lines[i]);
			}
			Lines.Clear();
		}
		void HideShowLines() {
			if (!ShowLines) return;
			for (int i = 0; i < Lines.Count; i++) {
				if (i < LineCount) {
					MakeLineVisible(Lines[i]);
				} else {
					MakeLineInvisible(Lines[i]);
				}
			}
		}
		void UpdateCachedLines() {
			CachedLines.Clear();
			for (int i = 0; i < Lines.Count; i++) {
				CachedLines.Add(Lines[i], true);
			}
		}
		internal FrameworkElement GetElementForLine(int lineIndex, IList<UIElement> sortedChildren) {
			return sortedChildren[lineIndex] as FrameworkElement;
		}
		Rectangle GetLineForElement(int elementIndex) {
			if (elementIndex < 0 || elementIndex >= LineCount || elementIndex >= Lines.Count) return null;
			return Lines[elementIndex];
		}
		void MakeLineVisible(FrameworkElement line) {
			line.Visibility = Visibility.Visible;
			SetLineVisibility(line, LinesWidth);
		}
		void MakeLineInvisible(FrameworkElement line) {
			line.Visibility = Visibility.Collapsed;
			SetLineVisibility(line, 0);
		}
		void SetLineVisibility(FrameworkElement line, double lineWidth) {
			if (IsVertical) {
				line.Height = lineWidth;
			} else {
				line.Width = lineWidth;
			}
		}
		void SetLineWidth(FrameworkElement line, int lineIndex, IList<UIElement> sortedChildren) {
			Size size = GetLineSize(lineIndex, sortedChildren);
			Thickness margin = GetLineMargines(lineIndex, sortedChildren);
			if (IsVertical) {
				double res = size.Width - margin.Left - margin.Right;
				line.Width = res > 0 ? res : 0;
				line.Margin = new Thickness(margin.Left, 0, margin.Right, 0);
			} else {
				double res = size.Height - margin.Top - margin.Bottom;
				line.Height = res > 0 ? res : 0;
				line.Margin = new Thickness(0, margin.Top, 0, margin.Bottom);
			}
			line.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
		}
		Size GetLineSize(int lineIndex, IList<UIElement> sortedChildren) {
			if (StretchLines == AgStretchLine.ByPanel) return ChildMaxSize;
			FrameworkElement prevElement = GetElementForLine(lineIndex, sortedChildren);
			Size result = prevElement.DesiredSize;
			if (StretchLines == AgStretchLine.ByPreviousAndNextElements && (lineIndex + 1 < sortedChildren.Count)) {
				FrameworkElement nextElement = GetElementForLine(lineIndex + 1, sortedChildren);
				result.Width = Math.Max(result.Width, nextElement.DesiredSize.Width);
				result.Height = Math.Max(result.Height, nextElement.DesiredSize.Height);
			}
			return result;
		}
		Thickness GetLineMargines(int lineIndex, IList<UIElement> sortedChildren) {
			if (StretchLines == AgStretchLine.ByPanel) return new Thickness(LinesMargin, LinesMargin, LinesMargin, LinesMargin);
			FrameworkElement prevElement = GetElementForLine(lineIndex, sortedChildren);
			Thickness result = prevElement.Margin;
			if (StretchLines == AgStretchLine.ByPreviousAndNextElements && (lineIndex + 1 < sortedChildren.Count)) {
				FrameworkElement nextElement = GetElementForLine(lineIndex + 1, sortedChildren);
				result.Left = Math.Min(result.Left, nextElement.Margin.Left);
				result.Top = Math.Min(result.Top, nextElement.Margin.Top);
				result.Right = Math.Min(result.Right, nextElement.Margin.Right);
				result.Bottom = Math.Min(result.Bottom, nextElement.Margin.Bottom);
			}
			return result;
		}
		List<Rectangle> GetExistingLines() {
			List<Rectangle> result = new List<Rectangle>();
			foreach (UIElement element in Children) {
				if (IsPanelLine(element)) {
					result.Add(element as Rectangle);
				}
			}
			return result;
		}
		#region IAgStackPanelChildrenOwner Members
		UIElementCollection IAgStackPanelChildrenOwner.Children { get { return Children; } }
		bool IAgStackPanelChildrenOwner.IsLine(Rectangle rect) { return Lines.Contains(rect); }
		#endregion
	}
	public class AgIndentStackPanel : AgStackPanel {
		public const double DefaultIndent = 10, DefaultEmptySize = 33;
		public static readonly DependencyProperty IndentProperty = DependencyProperty.Register("Indent", typeof(double),
			typeof(AgIndentStackPanel), new PropertyMetadata(OnIndentPropertyChanged));
		public static readonly DependencyProperty EmptySizeProperty = DependencyProperty.Register("EmptySize", typeof(double),
			typeof(AgIndentStackPanel), new PropertyMetadata(OnEmptySizePropertyChanged));
		static void OnIndentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			if (d.IsHandlerSuspended(OrientationProperty)) return;
			AgIndentStackPanel panel = d as AgIndentStackPanel;
			if (panel == null) return;
			panel.OnIndentChanged((double)e.NewValue);
		}
		static void OnEmptySizePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
			if (d.IsHandlerSuspended(EmptySizeProperty)) return;
			AgIndentStackPanel panel = d as AgIndentStackPanel;
			if (panel == null) return;
			panel.OnEmptySizeChanged((double)e.NewValue);
		}
		public AgIndentStackPanel() : this(Orientation.Horizontal) { }
		public AgIndentStackPanel(Orientation orientation)
			: base(orientation) {
			this.SetValueNoCallback(IndentProperty, DefaultIndent);
			this.SetValueNoCallback(EmptySizeProperty, DefaultEmptySize);
		}
		protected virtual void OnIndentChanged(double value) {
			this.InvalidateMeasureEx();
		}
		protected virtual void OnEmptySizeChanged(double value) {
			this.InvalidateMeasureEx();
		}
		public double Indent {
			get { return (double)GetValue(IndentProperty); }
			set { SetValue(IndentProperty, value); }
		}
		public double EmptySize {
			get { return (double)GetValue(EmptySizeProperty); }
			set { SetValue(EmptySizeProperty, value); }
		}
		protected double TotalIndent {
			get {
				if (Children.Count == 0) return 0;
				return (Children.Count - 1) * Indent;
			}
		}
		protected override Size MeasureSortedChildrenOverride(Size availableSize, IList<UIElement> sortedChildren) {
			Size res = base.MeasureSortedChildrenOverride(availableSize, sortedChildren);
			if (sortedChildren.Count == 0 || Visibility == Visibility.Collapsed) {
				if (IsVertical)
					res.Width = EmptySize;
				else
					res.Height = EmptySize;
			}
			if (IsVertical)
				res.Width += TotalIndent;
			else
				res.Height += TotalIndent;
			return res;
		}
		protected override Rect ArrangeChild(UIElement child, int childIndex, Rect childRect) {
			if (IsVertical)
				childRect.X += childIndex * Indent;
			else
				childRect.Y += childIndex * Indent;
			return base.ArrangeChild(child, childIndex, childRect);
		}
	}
	public class AgFreeSpaceAvailableEventArgs : EventArgs {
		Size finalSize;
		double sizeAvailable;
		public AgFreeSpaceAvailableEventArgs(Size finalSize, double sizeAvailable) {
			this.finalSize = finalSize;
			this.sizeAvailable = sizeAvailable;
		}
		public Size FinalSize { get { return finalSize; } }
		public double SizeAvailable { get { return sizeAvailable; } }
	}
}
